References and ports
Contents
References and ports#
GDS allows your the component once in memory and Reference or Instance the component multiple times.
gdstk and gdspy calls it Reference and klayout calls it Instance
As you build components you can include references to other components. Adding a reference is like having a pointer to a component.
The GDSII specification allows the use of references, and similarly gdsfactory uses them (with the add_ref() function). what is a reference? Simply put: A reference does not contain any geometry. It only points to an existing geometry.
Say you have a ridiculously large polygon with 100 billion vertices that you call BigPolygon. It’s huge, and you need to use it in your design 250 times. Well, a single copy of BigPolygon takes up 1MB of memory, so you don’t want to make 250 copies of it You can instead references the polygon 250 times. Each reference only uses a few bytes of memory – it only needs to know the memory address of BigPolygon, position, rotation and mirror. This way, you can keep one copy of BigPolygon and use it again and again.
You can start by making a blank Component and add a single polygon to it.
[1]:
import gdsfactory as gf
gf.config.rich_output()
PDK = gf.generic_tech.get_generic_pdk()
PDK.activate()
# Create a blank Component
p = gf.Component("component_with_polygon")
# Add a polygon
xpts = [0, 0, 5, 6, 9, 12]
ypts = [0, 1, 1, 2, 2, 0]
p.add_polygon([xpts, ypts], layer=(2, 0))
# plot the Component with the polygon in it
p
2023-02-20 17:44:58.450 | INFO | gdsfactory.config:<module>:50 - Load '/home/runner/work/gdsfactory/gdsfactory/gdsfactory' 6.43.1
2023-02-20 17:44:59.339 | INFO | gdsfactory.technology.layer_views:__init__:785 - Importing LayerViews from YAML file: /home/runner/work/gdsfactory/gdsfactory/gdsfactory/generic_tech/layer_views.yaml.
2023-02-20 17:44:59.346 | INFO | gdsfactory.pdk:activate:206 - 'generic' PDK is now active
component_with_polygon: uid df2f1ee1, ports [], references [], 1 polygons
Now, you want to reuse this polygon repeatedly without creating multiple copies of it.
To do so, you need to make a second blank Component, this time called c.
In this new Component you reference our Component p which contains our polygon.
[2]:
c = gf.Component("Component_with_references") # Create a new blank Component
poly_ref = c.add_ref(p) # Reference the Component "p" that has the polygon in it
c
Component_with_references: uid 3d58bc3a, ports [], references ['component_with_polygon_1'], 0 polygons
you just made a copy of your polygon – but remember, you didn’t actually make a second polygon, you just made a reference (aka pointer) to the original polygon. Let’s add two more references to c:
[3]:
poly_ref2 = c.add_ref(p) # Reference the Component "p" that has the polygon in it
poly_ref3 = c.add_ref(p) # Reference the Component "p" that has the polygon in it
c
Component_with_references: uid 3d58bc3a, ports [], references ['component_with_polygon_1', 'component_with_polygon_2', 'component_with_polygon_3'], 0 polygons
Now you have 3x polygons all on top of each other. Again, this would appear useless, except that you can manipulate each reference independently. Notice that when you called c.add_ref(p) above, we saved the result to a new variable each time (poly_ref, poly_ref2, and poly_ref3)? You can use those variables to reposition the references.
[4]:
poly_ref2.rotate(15) # Rotate the 2nd reference we made 15 degrees
poly_ref3.rotate(30) # Rotate the 3rd reference we made 30 degrees
c
Component_with_references: uid 3d58bc3a, ports [], references ['component_with_polygon_1', 'component_with_polygon_2', 'component_with_polygon_3'], 0 polygons
Now you’re getting somewhere! You’ve only had to make the polygon once, but you’re able to reuse it as many times as you want.
Modifying the referenced geometry#
What happens when you change the original geometry that the reference points to? In your case, your references in c all point to the Component p that with the original polygon. Let’s try adding a second polygon to p.
First you add the second polygon and make sure P looks like you expect:
[5]:
# Add a 2nd polygon to "p"
xpts = [14, 14, 16, 16]
ypts = [0, 2, 2, 0]
p.add_polygon([xpts, ypts], layer=(1, 0))
p
component_with_polygon: uid df2f1ee1, ports [], references [], 2 polygons
That looks good. Now let’s find out what happened to c that contains the three references. Keep in mind that you have not modified c or executed any functions/operations on c – all you have done is modify p.
[6]:
c
Component_with_references: uid 3d58bc3a, ports [], references ['component_with_polygon_1', 'component_with_polygon_2', 'component_with_polygon_3'], 0 polygons
When you modify the original geometry, all of the references automatically reflect the modifications. This is very powerful, because you can use this to make very complicated designs from relatively simple elements in a computation- and memory-efficient way.
Let’s try making references a level deeper by referencing c. Note here we use the << operator to add the references – this is just shorthand, and is exactly equivalent to using add_ref()
[7]:
c2 = gf.Component("array_sample") # Create a new blank Component
d_ref1 = c2.add_ref(c) # Reference the Component "c" that 3 references in it
d_ref2 = c2 << c # Use the "<<" operator to create a 2nd reference to c
d_ref3 = c2 << c # Use the "<<" operator to create a 3rd reference to c
d_ref1.move([20, 0])
d_ref2.move([40, 0])
c2
array_sample: uid ea7f0ab8, ports [], references ['Component_with_references_1', 'Component_with_references_2', 'Component_with_references_3'], 0 polygons
As you’ve seen you have two ways to add a reference to our component:
create the reference and add it to the component
[8]:
c = gf.Component("reference_sample")
w = gf.components.straight(width=0.6)
wr = w.ref()
c.add(wr)
c
reference_sample: uid d11aaebd, ports [], references ['straight_1'], 0 polygons
or do it in a single line
[9]:
c = gf.Component("reference_sample_shorter_syntax")
wr = c << gf.components.straight(width=0.6)
c
reference_sample_shorter_syntax: uid abbb5195, ports [], references ['straight_1'], 0 polygons
in both cases you can move the reference wr after created
[10]:
c = gf.Component("two_references")
wr1 = c << gf.components.straight(width=0.6)
wr2 = c << gf.components.straight(width=0.6)
wr2.movey(10)
c.add_ports(wr1.get_ports_list(), prefix="top_")
c.add_ports(wr2.get_ports_list(), prefix="bot_")
[11]:
c.ports
{ 'top_o1': Port (name top_o1, center [0. 0.], width 0.6, orientation 180.0, layer (1, 0), port_type optical), 'top_o2': Port (name top_o2, center [10. 0.], width 0.6, orientation 0.0, layer (1, 0), port_type optical), 'bot_o1': Port (name bot_o1, center [ 0. 10.], width 0.6, orientation 180.0, layer (1, 0), port_type optical), 'bot_o2': Port (name bot_o2, center [10. 10.], width 0.6, orientation 0.0, layer (1, 0), port_type optical) }
You can also auto_rename ports using gdsfactory default convention, where ports are numbered clockwise starting from the bottom left
[12]:
c.auto_rename_ports()
[13]:
c.ports
{ 'o1': Port (name o1, center [0. 0.], width 0.6, orientation 180.0, layer (1, 0), port_type optical), 'o4': Port (name o4, center [10. 0.], width 0.6, orientation 0.0, layer (1, 0), port_type optical), 'o2': Port (name o2, center [ 0. 10.], width 0.6, orientation 180.0, layer (1, 0), port_type optical), 'o3': Port (name o3, center [10. 10.], width 0.6, orientation 0.0, layer (1, 0), port_type optical) }
[14]:
c
two_references: uid a36002d4, ports ['o1', 'o4', 'o2', 'o3'], references ['straight_1', 'straight_2'], 0 polygons
Arrays of references#
In GDS, there’s a type of structure called a “ComponentReference” which takes a cell and repeats it NxM times on a fixed grid spacing. For convenience, Component includes this functionality with the add_array() function. Note that CellArrays are not compatible with ports (since there is no way to access/modify individual elements in a GDS cellarray)
gdsfactory also provides with more flexible arrangement options if desired, see for example grid() and packer().
As well as gf.components.array
Let’s make a new Component and put a big array of our Component c in it:
[15]:
c3 = gf.Component("array_of_references") # Create a new blank Component
aref = c3.add_array(
c, columns=6, rows=3, spacing=[20, 15]
) # Reference the Component "c" 3 references in it with a 3 rows, 6 columns array
c3
array_of_references: uid 3e090c63, ports [], references ['two_references_1'], 0 polygons
CellArrays don’t have ports and there is no way to access/modify individual elements in a GDS cellarray.
gdsfactory provides you with similar functions in gf.components.array and gf.components.array_2d
[16]:
c4 = gf.Component("demo_array") # Create a new blank Component
aref = c4 << gf.components.array(component=c, columns=3, rows=2)
c4.add_ports(aref.get_ports_list())
c4
demo_array: uid f1920952, ports ['o1_1_1', 'o1_1_2', 'o1_1_3', 'o2_1_1', 'o2_1_2', 'o2_1_3', 'o1_2_1', 'o1_2_2', 'o1_2_3', 'o2_2_1', 'o2_2_2', 'o2_2_3', 'o3_2_1', 'o3_2_2', 'o3_2_3', 'o4_2_1', 'o4_2_2', 'o4_2_3', 'o3_1_1', 'o3_1_2', 'o3_1_3', 'o4_1_1', 'o4_1_2', 'o4_1_3'], references ['array_1'], 0 polygons
[17]:
gf.components.array?
You can also create an array of references for periodic structures. Lets create a Distributed Bragg Reflector
[18]:
@gf.cell
def dbr_period(w1=0.5, w2=0.6, l1=0.2, l2=0.4, straight=gf.components.straight):
"""Return one DBR period."""
c = gf.Component()
r1 = c << straight(length=l1, width=w1)
r2 = c << straight(length=l2, width=w2)
r2.connect(port="o1", destination=r1.ports["o2"])
c.add_port("o1", port=r1.ports["o1"])
c.add_port("o2", port=r2.ports["o2"])
return c
l1 = 0.2
l2 = 0.4
n = 3
period = dbr_period(l1=l1, l2=l2)
period
dbr_period: uid 726221ad, ports ['o1', 'o2'], references ['straight_1', 'straight_2'], 0 polygons
[19]:
dbr = gf.Component("DBR")
dbr.add_array(period, columns=n, rows=1, spacing=(l1 + l2, 100))
dbr
DBR: uid 5d69841a, ports [], references ['dbr_period_1'], 0 polygons
Finally we need to add ports to the new component
[20]:
p0 = dbr.add_port("o1", port=period.ports["o1"])
p1 = dbr.add_port("o2", port=period.ports["o2"])
p1.center = [(l1 + l2) * n, 0]
dbr
DBR: uid 5d69841a, ports ['o1', 'o2'], references ['dbr_period_1'], 0 polygons
Connect references#
We have seen that once you create a reference you can manipulate the reference to move it to a location. Here we are going to connect that reference to a port. Remember that we follow that a certain reference source connects to a destination port
[21]:
bend = gf.components.bend_circular()
bend
bend_circular: uid 71ccd4c8, ports ['o1', 'o2'], references [], 1 polygons
[22]:
c = gf.Component("sample_reference_connect")
mmi = c << gf.components.mmi1x2()
b = c << gf.components.bend_circular()
b.connect("o1", destination=mmi.ports["o2"])
c.add_port("o1", port=mmi.ports["o1"])
c.add_port("o2", port=b.ports["o2"])
c.add_port("o3", port=mmi.ports["o3"])
c
sample_reference_connect: uid 2d519ccb, ports ['o1', 'o2', 'o3'], references ['mmi1x2_1', 'bend_circular_1'], 0 polygons
You can also access the ports directly from the references
[23]:
c = gf.Component("sample_reference_connect_simpler")
mmi = c << gf.components.mmi1x2()
b = c << gf.components.bend_circular()
b.connect("o1", destination=mmi["o2"])
c.add_port("o1", port=mmi["o1"])
c.add_port("o2", port=b["o2"])
c.add_port("o3", port=mmi["o3"])
c
sample_reference_connect_simpler: uid a03397cb, ports ['o1', 'o2', 'o3'], references ['mmi1x2_1', 'bend_circular_1'], 0 polygons
Port naming#
You have the freedom to name the ports as you want, and you can use gf.port.auto_rename_ports(prefix='o') to rename them later on.
Here is the default naming convention.
Ports are numbered clock-wise starting from the bottom left corner
Optical ports have o prefix and Electrical ports e prefix
The port naming comes in most cases from the gdsfactory.cross_section. For example
gdsfactory.cross_section.striphas portso1for input ando2for outputgdsfactory.cross_section.metal1has portse1for input ande2for output
[24]:
size = 4
c = gf.components.nxn(west=2, south=2, north=2, east=2, xsize=size, ysize=size)
c
nxn_afe554f8: uid 99855968, ports ['o1', 'o2', 'o6', 'o5', 'o3', 'o4', 'o8', 'o7'], references ['rectangle_1'], 0 polygons
[25]:
c = gf.components.straight_heater_metal(length=30)
c
straight_heater_metal_u_3ff09402: uid 4909114c, ports ['o1', 'o2', 'e1', 'e2'], references ['component_sequence_1', 'via_stack_1', 'via_stack_2', 'taper_1', 'taper_2'], 0 polygons
[26]:
c.ports
{ 'o1': Port (name o1, center [0. 0.], width 0.5, orientation 180.0, layer (1, 0), port_type optical), 'o2': Port (name o2, center [30. 0.], width 0.5, orientation 0.0, layer (1, 0), port_type optical), 'e1': Port (name e1, center [-16. 0.], width 11.0, orientation 180.0, layer (49, 0), port_type electrical), 'e2': Port (name e2, center [46. 0.], width 11.0, orientation 0.0, layer (49, 0), port_type electrical) }
You can get the optical ports by layer
[27]:
c.get_ports_dict(layer=(1, 0))
{ 'o1': Port (name o1, center [0. 0.], width 0.5, orientation 180.0, layer (1, 0), port_type optical), 'o2': Port (name o2, center [30. 0.], width 0.5, orientation 0.0, layer (1, 0), port_type optical) }
or by width
[28]:
c.get_ports_dict(width=0.5)
{ 'o1': Port (name o1, center [0. 0.], width 0.5, orientation 180.0, layer (1, 0), port_type optical), 'o2': Port (name o2, center [30. 0.], width 0.5, orientation 0.0, layer (1, 0), port_type optical) }
[29]:
c0 = gf.components.straight_heater_metal()
c0.ports
{ 'o1': Port (name o1, center [0. 0.], width 0.5, orientation 180.0, layer (1, 0), port_type optical), 'o2': Port (name o2, center [320. 0.], width 0.5, orientation 0.0, layer (1, 0), port_type optical), 'e1': Port (name e1, center [-16. 0.], width 11.0, orientation 180.0, layer (49, 0), port_type electrical), 'e2': Port (name e2, center [336. 0.], width 11.0, orientation 0.0, layer (49, 0), port_type electrical) }
[30]:
c1 = c0.copy()
c1.auto_rename_ports_layer_orientation()
c1.ports
{ '1_0_W0': Port (name 49_0_W1, center [0. 0.], width 0.5, orientation 180.0, layer (1, 0), port_type optical), '1_0_E0': Port (name 49_0_E0, center [320. 0.], width 0.5, orientation 0.0, layer (1, 0), port_type optical), '49_0_W0': Port (name 49_0_W0, center [-16. 0.], width 11.0, orientation 180.0, layer (49, 0), port_type electrical), '49_0_E1': Port (name 49_0_E1, center [336. 0.], width 11.0, orientation 0.0, layer (49, 0), port_type electrical) }
[31]:
c2 = c0.copy()
c2.auto_rename_ports()
c2.ports
{ 'o1': Port (name o1, center [0. 0.], width 0.5, orientation 180.0, layer (1, 0), port_type optical), 'o2': Port (name o2, center [320. 0.], width 0.5, orientation 0.0, layer (1, 0), port_type optical), 'e1': Port (name e1, center [-16. 0.], width 11.0, orientation 180.0, layer (49, 0), port_type electrical), 'e2': Port (name e2, center [336. 0.], width 11.0, orientation 0.0, layer (49, 0), port_type electrical) }
You can also rename them with a different port naming convention
prefix: add
efor electricalofor opticalclockwise
counter-clockwise
orientation
EEast,WWest,NNorth,SSouth
Here is the default one we use (clockwise starting from bottom left west facing port)
3 4
|___|_
2 -| |- 5
| |
1 -|______|- 6
| |
8 7
[32]:
c = gf.Component("demo_ports")
nxn = gf.components.nxn(west=2, north=2, east=2, south=2, xsize=4, ysize=4)
ref = c.add_ref(nxn)
c.add_ports(ref.ports)
c
demo_ports: uid e1394513, ports ['o1', 'o2', 'o6', 'o5', 'o3', 'o4', 'o8', 'o7'], references ['nxn_1'], 0 polygons
[33]:
ref.get_ports_list() # by default returns ports clockwise starting from bottom left west facing port
[ Port (name o1, center [0. 1.25], width 0.5, orientation 180.0, layer (1, 0), port_type optical), Port (name o2, center [0. 2.75], width 0.5, orientation 180.0, layer (1, 0), port_type optical), Port (name o3, center [1.25 4. ], width 0.5, orientation 90.0, layer (1, 0), port_type optical), Port (name o4, center [2.75 4. ], width 0.5, orientation 90.0, layer (1, 0), port_type optical), Port (name o5, center [4. 2.75], width 0.5, orientation 0.0, layer (1, 0), port_type optical), Port (name o6, center [4. 1.25], width 0.5, orientation 0.0, layer (1, 0), port_type optical), Port (name o7, center [2.75 0. ], width 0.5, orientation 270.0, layer (1, 0), port_type optical), Port (name o8, center [1.25 0. ], width 0.5, orientation 270.0, layer (1, 0), port_type optical) ]
[34]:
c.auto_rename_ports()
c
demo_ports: uid e1394513, ports ['o1', 'o2', 'o6', 'o5', 'o3', 'o4', 'o8', 'o7'], references ['nxn_1'], 0 polygons
You can also get the ports counter-clockwise
4 3
|___|_
5 -| |- 2
| |
6 -|______|- 1
| |
7 8
[35]:
c.auto_rename_ports_counter_clockwise()
c
demo_ports: uid e1394513, ports ['o6', 'o5', 'o1', 'o2', 'o4', 'o3', 'o7', 'o8'], references ['nxn_1'], 0 polygons
[36]:
c.get_ports_list(clockwise=False)
[ Port (name o1, center [4. 1.25], width 0.5, orientation 0.0, layer (1, 0), port_type optical), Port (name o2, center [4. 2.75], width 0.5, orientation 0.0, layer (1, 0), port_type optical), Port (name o3, center [2.75 4. ], width 0.5, orientation 90.0, layer (1, 0), port_type optical), Port (name o4, center [1.25 4. ], width 0.5, orientation 90.0, layer (1, 0), port_type optical), Port (name o5, center [0. 2.75], width 0.5, orientation 180.0, layer (1, 0), port_type optical), Port (name o6, center [0. 1.25], width 0.5, orientation 180.0, layer (1, 0), port_type optical), Port (name o7, center [1.25 0. ], width 0.5, orientation 270.0, layer (1, 0), port_type optical), Port (name o8, center [2.75 0. ], width 0.5, orientation 270.0, layer (1, 0), port_type optical) ]
[37]:
c.ports_layer
{ '1_0_W0': 'o6', '1_0_W1': 'o5', 'o1': 'o1', 'o2': 'o2', '1_0_N0': 'o4', '1_0_N1': 'o3', '1_0_S0': 'o7', '1_0_S1': 'o8' }
[38]:
c.port_by_orientation_cw("W0")
Port (name o6, center [0. 1.25], width 0.5, orientation 180.0, layer (1, 0), port_type optical)
[39]:
c.port_by_orientation_ccw("W1")
Port (name o6, center [0. 1.25], width 0.5, orientation 180.0, layer (1, 0), port_type optical)
Lets extend the East facing ports (orientation = 0 deg)
[40]:
cross_section = gf.cross_section.strip()
nxn = gf.components.nxn(
west=2, north=2, east=2, south=2, xsize=4, ysize=4, cross_section=cross_section
)
c = gf.components.extension.extend_ports(component=nxn, orientation=0)
c
nxn_a4cd1236_extend_por_cffc10ff: uid 84dd9e83, ports ['o1', 'o2', 'o3', 'o4', 'o5', 'o6', 'o7', 'o8'], references ['nxn_1', 'straight_1', 'straight_2'], 0 polygons
[41]:
c.ports
{ 'o1': Port (name o1, center [0. 1.25], width 0.5, orientation 180, layer (1, 0), port_type optical), 'o2': Port (name o2, center [0. 2.75], width 0.5, orientation 180, layer (1, 0), port_type optical), 'o3': Port (name o3, center [1.25 4. ], width 0.5, orientation 90, layer (1, 0), port_type optical), 'o4': Port (name o4, center [2.75 4. ], width 0.5, orientation 90, layer (1, 0), port_type optical), 'o5': Port (name o5, center [9. 2.75], width 0.5, orientation 0.0, layer (1, 0), port_type optical), 'o6': Port (name o6, center [9. 1.25], width 0.5, orientation 0.0, layer (1, 0), port_type optical), 'o7': Port (name o7, center [2.75 0. ], width 0.5, orientation 270, layer (1, 0), port_type optical), 'o8': Port (name o8, center [1.25 0. ], width 0.5, orientation 270, layer (1, 0), port_type optical) }
[42]:
df = c.get_ports_pandas()
df
[42]:
| name | width | center | orientation | layer | port_type | shear_angle | |
|---|---|---|---|---|---|---|---|
| 0 | o1 | 0.5 | [0.0, 1.25] | 180.0 | [1, 0] | optical | None |
| 1 | o2 | 0.5 | [0.0, 2.75] | 180.0 | [1, 0] | optical | None |
| 2 | o3 | 0.5 | [1.25, 4.0] | 90.0 | [1, 0] | optical | None |
| 3 | o4 | 0.5 | [2.75, 4.0] | 90.0 | [1, 0] | optical | None |
| 4 | o5 | 0.5 | [9.0, 2.75] | 0.0 | [1, 0] | optical | None |
| 5 | o6 | 0.5 | [9.0, 1.25] | 0.0 | [1, 0] | optical | None |
| 6 | o7 | 0.5 | [2.75, 0.0] | 270.0 | [1, 0] | optical | None |
| 7 | o8 | 0.5 | [1.25, 0.0] | 270.0 | [1, 0] | optical | None |
[43]:
df[df.port_type == 'optical']
[43]:
| name | width | center | orientation | layer | port_type | shear_angle | |
|---|---|---|---|---|---|---|---|
| 0 | o1 | 0.5 | [0.0, 1.25] | 180.0 | [1, 0] | optical | None |
| 1 | o2 | 0.5 | [0.0, 2.75] | 180.0 | [1, 0] | optical | None |
| 2 | o3 | 0.5 | [1.25, 4.0] | 90.0 | [1, 0] | optical | None |
| 3 | o4 | 0.5 | [2.75, 4.0] | 90.0 | [1, 0] | optical | None |
| 4 | o5 | 0.5 | [9.0, 2.75] | 0.0 | [1, 0] | optical | None |
| 5 | o6 | 0.5 | [9.0, 1.25] | 0.0 | [1, 0] | optical | None |
| 6 | o7 | 0.5 | [2.75, 0.0] | 270.0 | [1, 0] | optical | None |
| 7 | o8 | 0.5 | [1.25, 0.0] | 270.0 | [1, 0] | optical | None |
pins#
You can add pins (port markers) to each port. Each foundry PDK does this differently, so gdsfactory supports all of them.
square with port inside the component
square centered (half inside, half outside component)
triangular
path (SiEPIC)
by default Component.show() will add triangular pins, so you can see the direction of the port in Klayout.
[44]:
gf.components.mmi1x2(decorator=gf.add_pins.add_pins)
mmi1x2_1514d1a3: uid 0e853bcc, ports ['o1', 'o2', 'o3'], references [], 7 polygons
[45]:
gf.components.mmi1x2(decorator=gf.add_pins.add_pins_triangle)
mmi1x2_6292428a: uid 0215f725, ports ['o1', 'o2', 'o3'], references [], 7 polygons
component_sequence#
When you have repetitive connections you can describe the connectivity as an ASCII map
[46]:
bend180 = gf.components.bend_circular180()
wg_pin = gf.components.straight_pin(length=40)
wg = gf.components.straight()
# Define a map between symbols and (component, input port, output port)
symbol_to_component = {
"D": (bend180, "o1", "o2"),
"C": (bend180, "o2", "o1"),
"P": (wg_pin, "o1", "o2"),
"-": (wg, "o1", "o2"),
}
# Generate a sequence
# This is simply a chain of characters. Each of them represents a component
# with a given input and and a given output
sequence = "DC-P-P-P-P-CD"
component = gf.components.component_sequence(
sequence=sequence, symbol_to_component=symbol_to_component
)
component.name = "component_sequence"
component
component_sequence: uid e816d870, ports ['o1', 'o2'], references ['D1', 'C1', '-1', 'P1', '-2', 'P2', '-3', 'P3', '-4', 'P4', '-5', 'C2', 'D2'], 0 polygons
As the sequence is defined as a string you can use the string operations to easily build complex sequences